"""
This module contains the interface for building plugins (ConduitPlugin class) and
some builtin plugins.
"""
from string import Template
import django.forms as forms
from django.conf import settings
from django.conf.urls.defaults import url
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect, HttpResponse
from socialauth.models import TwitterUserProfile
import oauth.oauth as oauth
try:
    from oauthtwitter import OAuthApi
except:
    from socialauth.lib.oauthtwitter import OAuthApi
import twitter
from instafeed.utils import CustomAsUlFormMixin
from instafeed.utils import reduceLink, unescape, html2txt
from instafeed.conduits.plugins import ConduitPlugin
from socialauth.lib import oauthtwitter2


class TwitterConduitBase(ConduitPlugin):

    def __init__(self, messageFormat="$description"):
        self.messageFormat = messageFormat

    def _postMessage(self, api, subscription, feed):
        if self.messageFormat == "auto":
            messageFormatStr = "$description"
        else:
            messageFormatStr = self.messageFormat
        messageParams = {
            'subcriptionName': subscription.name,
            'title': html2txt(unescape(feed.title)).encode("utf-8"),
            'link': feed.link,
            'description': html2txt(unescape(feed.description)).encode("utf-8"),
        }
        messageFormat = Template(messageFormatStr)
        message = messageFormat.safe_substitute(messageParams)
        if len(message) > 140:
            messageParams["link"] = reduceLink(messageParams["link"])
            message = messageFormat.safe_substitute(messageParams)
        if len(message) > 140 and not "$link" in messageFormatStr:
            # If message exceeds the size, we will add link to the message
            messageFormatStr += " - $link"
            messageFormat = Template(messageFormatStr)
            message = messageFormat.safe_substitute(messageParams)
        if len(message) > 140: 
            maxSize = len(messageParams["description"]) - (len(message) + len(" ...") - 140)
            messageParams["description"] = ' '.join(messageParams["description"][:maxSize + 1].split(' ')[0:-1])
            message = messageFormat.safe_substitute(messageParams)
        if len(message) > 140:
            maxSize = len(messageParams["title"]) - (len(message) + len(" ...") - 140)
            messageParams["title"] = ' '.join(messageParams["title"][:maxSize + 1].split(' ')[0:-1])
            message = messageFormat.safe_substitute(messageParams)
        print "Posting message to twitter: ", message.encode("latin-1", "ignore")
        api.PostUpdate(message.encode("utf-8")[:140])


class TwitterUserOAuthConduit(TwitterConduitBase):
    """
    Sends feeds updates to twitter using user's twitter account.
    Uses OAuth authentication. Only works if the user logged in with his twitter account
    """

    key = "twitterUserOAuth"
    name = "Twitter using user's account"

    allowNew = False # Only autoAdd

    def sendFeed(self, subscription, feed):
        user = subscription.user
        twitterProfile = TwitterUserProfile.objects.get(user=user) # Must exist, this plugin should
            # only be used by users logged in with their twitter account.
        access_token = oauth.OAuthToken.from_string(twitterProfile.access_token)
        api = OAuthApi(settings.TWITTER_CONSUMER_KEY, settings.TWITTER_CONSUMER_SECRET, access_token)
        return self._postMessage(api, subscription, feed)

    @classmethod
    def autoAddConduits(klass, user, currentConduits):
        from instafeed.conduits.models import Conduit
        if currentConduits.filter(plugin="twitterUserOAuth"):
            return # Conduit already added
        try:
            profile = TwitterUserProfile.objects.get(user=user)
        except TwitterUserProfile.DoesNotExist:
            return # The user login isn't twitter
        name = "Twitter @%s" % user.username[len("twitter_"):]
        newConduit = Conduit(user=user, name=name, plugin=klass.key,
            params="{}")
        newConduit.save()


class TwitterAPIConduit(TwitterConduitBase):
    """
    Sends feeds updates to twitter using provided username and password.
    Uses simple authentication (not OAuth).
    """

    key = "twitterSimple"
    name = "Twitter with username/password"

    template = "conduits/twitter_api.html"

    def __init__(self, username, password, messageFormat="$description"):
        TwitterConduitBase.__init__(self, messageFormat)
        self.username = username
        self.password = password

    def sendFeed(self, subscription, feed):
        api = twitter.Api(username=self.username, password=self.password)
        return self._postMessage(api, subscription, feed)

    class Form(CustomAsUlFormMixin, forms.Form):
        username = forms.CharField()
        password = forms.CharField(widget=forms.PasswordInput())
        messageFormat = forms.CharField(initial="$description",
            help_text="Format of the message posted.\n"
                "You can use a custom message format, using variables $title, $link, $description and "
                "$subscriptionName. If the message doesn't fits we cut it and we add the link.")

    @classmethod
    def update_template_params(klass, request, params):
        if not "twitter_api_form" in params:
            params["twitter_api_form"] = klass.Form(prefix=klass.key)

    @classmethod
    def validate_new(klass, request):
        form = klass.Form(request.POST, prefix=klass.key)
        if form.is_valid():
            return True, {'username': form.cleaned_data["username"],
                'password': form.cleaned_data["password"],
                'messageFormat': form.cleaned_data["messageFormat"],
                }
        else:
            return False, {'twitter_api_form': form}


class TwitterOAuthConduit(TwitterConduitBase):
    """
    Sends feeds updates to twitter using any twitter account.
    Uses OAuth authentication.
    """

    key = "twitterOAuth"
    name = "Twitter using oauth"

    allowNew = True

    template = "conduits/twitter_oauth.html"

    def __init__(self, access_token, messageFormat="$description"):
        TwitterConduitBase.__init__(self, messageFormat)
        self.access_token = access_token

    def sendFeed(self, subscription, feed):
        access_token = oauth.OAuthToken.from_string(self.access_token)
        api = OAuthApi(settings.TWITTER_OAUTH_CONSUMER_KEY, settings.TWITTER_OAUTH_CONSUMER_SECRET,
            access_token)
        return self._postMessage(api, subscription, feed)

    class Form(CustomAsUlFormMixin, forms.Form):
        authorization_key = forms.CharField(widget=forms.HiddenInput())
        twitter_user = forms.CharField(widget=forms.HiddenInput())
        messageFormat = forms.CharField(initial="$description",
            help_text="Format of the message posted.\n"
                "You can use a custom message format, using variables $title, $link, $description and "
                "$subscriptionName. If the message doesn't fits we cut it and we add the link.")

    @classmethod
    def update_template_params(klass, request, params):
        if not "twitter_oauth_form" in params:
            initial = {}
            if request.method == "POST" and "authorization_key" in request.POST:
                initial = {'authorization_key': request.POST["authorization_key"],
                    'twitter_user': request.POST["twitter_user"]}
                params["twitter_oauth_authenticated"] = True
                params["twitter_oauth_user"] = request.POST["twitter_user"]
            elif "access_token_key" in request.session:
                initial = {'authorization_key': request.session["access_token_key"],
                    "twitter_user": request.session["twitter_user"]}
                del request.session["access_token_key"] # Keep the key only in the form
                                                    # so is needed the form and the session
                                                    # to have a valid access_token
                params["twitter_oauth_user"] = request.session["twitter_user"]
                del request.session["twitter_user"]
                params["twitter_oauth_authenticated"] = True
            else:
                params["twitter_oauth_authenticated"] = False
            params["twitter_oauth_form"] = klass.Form(initial=initial, prefix=klass.key)

    @classmethod
    def validate_new(klass, request):
        form = klass.Form(request.POST, prefix=klass.key)
        if form.is_valid():
            # The form as authorization_key value
            access_token = oauth.OAuthToken(key=form.cleaned_data["authorization_key"],
                secret=request.session["access_token_secret"])

            return True, {'access_token': access_token.to_string(),
                'messageFormat': form.cleaned_data["messageFormat"],
                }
        else:
            return False, {'twitter_oauth_form': form}

    @classmethod
    def authorize_view(klass, request):
        TWITTER_OAUTH_CONSUMER_KEY = settings.TWITTER_OAUTH_CONSUMER_KEY
        TWITTER_OAUTH_CONSUMER_SECRET = settings.TWITTER_OAUTH_CONSUMER_SECRET
        twitterSrv = oauthtwitter2.TwitterOAuthClient(TWITTER_OAUTH_CONSUMER_KEY,
            TWITTER_OAUTH_CONSUMER_SECRET)
        request_token = twitterSrv.fetch_request_token()
        request.session['request_token'] = request_token.to_string()
        signin_url = twitterSrv.authorize_token_url(request_token)
        return HttpResponseRedirect(signin_url)

    @classmethod
    def authorization_done_view(klass, request):
        request_token = request.session.get('request_token', None)

        # If there is no request_token for session,
        #    means we didn't redirect user to twitter
        if not request_token:
            # Redirect the user to the login page,
            # So the user can click on the sign-in with twitter button
            print "We didn't redirected the user to twitter"
            print request.COOKIES, request.session, request.session.keys()
            return HttpResponse("We didn't redirect you to twitter....")

        token = oauth.OAuthToken.from_string(request_token)

        # If the token from session and token from twitter does not match
        #   means something bad happened to tokens
        if token.key != request.GET.get('oauth_token', 'no-token'):
            del request.session['request_token']
            # Redirect the user to the login page
            return HttpResponse("Something wrong! Tokens do not match...")

        twitter = oauthtwitter2.TwitterOAuthClient(settings.TWITTER_OAUTH_CONSUMER_KEY,
            settings.TWITTER_OAUTH_CONSUMER_SECRET)
        access_token = twitter.fetch_access_token(token)

        api = OAuthApi(settings.TWITTER_OAUTH_CONSUMER_KEY, settings.TWITTER_OAUTH_CONSUMER_SECRET,
            access_token)
        try:
            userinfo = api.GetUserInfo()
        except:
            # If we cannot get the user information, user cannot be authorized
            raise

        request.session['twitter_user'] = userinfo.screen_name
        request.session['access_token_key'] = access_token.key
        request.session['access_token_secret'] = access_token.secret
        del request.session['request_token']
        return HttpResponseRedirect(reverse("instafeed_new_conduit") + "?plugin=" + klass.key)

    @classmethod
    def get_urls(klass):
        return [
            url(r"^%s/authorize$" % klass.key, klass.authorize_view),
            url(r"^%s/authorization_done" % klass.key, klass.authorization_done_view),
            ]
